package br3akr.DeadlyDinnerParty.Server.Actions;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import br3akr.DeadlyDinnerParty.Server.Engine.Session;
import br3akr.DeadlyDinnerParty.Server.Exceptions.DeadlyDinnerPartyException;
import br3akr.DeadlyDinnerParty.Server.World.Area;
import br3akr.DeadlyDinnerParty.Server.World.Exit;
import br3akr.DeadlyDinnerParty.Server.World.GameCharacter;

public class MoveAction extends Action {
	boolean actionComplete;
    private ActionResponse actionResponse;
    public static final Set<String> keywords;
	static
	{
		Set<String>tempKeywords = new HashSet<String>();
		tempKeywords.add("West");
		tempKeywords.add("W");
		tempKeywords.add("North");
		tempKeywords.add("N");
		tempKeywords.add("South");
		tempKeywords.add("S");
		tempKeywords.add("East");
		tempKeywords.add("E");
		tempKeywords.add("NorthWest");
		tempKeywords.add("NW");
		tempKeywords.add("SouthWest");
		tempKeywords.add("SW");
		tempKeywords.add("NorthEast");
		tempKeywords.add("NE");
		tempKeywords.add("SouthEast");
		tempKeywords.add("SE");
		tempKeywords.add("Up");
		tempKeywords.add("Down");
		tempKeywords.add("Secret");
		keywords = Collections.unmodifiableSet(tempKeywords);
	}

    public MoveAction() {
        super("Move", false);
    }
    
    public ActionResponse doAction(String action, Session session) throws DeadlyDinnerPartyException {
		if (action == null) {
			actionComplete = true;
			throw new DeadlyDinnerPartyException ("No action found.");
		}
		if (session == null) {
			actionComplete = true;
			throw new DeadlyDinnerPartyException ("No session found.");
		}
		
		StringBuilder newDesc = new StringBuilder();
		GameCharacter actor = session.getCharacter();
		
		if (actor == null) {
			actionComplete = true;
			throw new DeadlyDinnerPartyException ("No character found for your session.");
		}
		
		if (actor.getStatus() == GameCharacter.Status.INCAPACITATED) {
			actionComplete = true;
			throw new DeadlyDinnerPartyException ("You can't move while incapacitated.");
		}
		
		Area curLocation = actor.getLocation();
		
		ActionResponse actionResponse = new ActionResponse();
		actionResponse.setActingCharacter(actor);
		
		Exit exit = curLocation.getExit(Exit.getDirection(action));
    	
		Area newLocation = null;
		Exit.ExitDirections exitDir = null;
		
		if (exit == null) {
			actionComplete = true;
			throw new DeadlyDinnerPartyException (action + " is not a valid exit.");
		}
		
		if (!action.equalsIgnoreCase("Secret")) {
			exitDir = exit.getExitDirection(curLocation);
			newLocation = exit.getExitArea(curLocation);
			
			if (newLocation == null) {
				actionComplete = true;
				throw new DeadlyDinnerPartyException ("There is no exit " + exitDir + " from here.");
			}
			
			if (exit.getStatus() != Exit.ExitStatus.OPEN && exit.getStatus() != Exit.ExitStatus.BASHED) {
				actionComplete = true;
				throw new DeadlyDinnerPartyException ("The exit " + exitDir + " to " + newLocation.getName() + " is closed.");
			}
		} else {
			if (!exit.knowsSecretExit(actor)) {
				actionComplete = true;
				throw new DeadlyDinnerPartyException ("You know of no secret exit from here.");
			}
			newLocation = curLocation.getAreaByExit(Exit.ExitDirections.SECRET);
		}
		
		actor.setLocation(null);
		curLocation.removeCharacter(actor);
		newLocation.addCharacter(actor);
		actor.setLocation(newLocation);
		Set<GameCharacter> curAreaOnlookers = curLocation.getAreaOnlookers(actor, null);
		Set<GameCharacter> newAreaOnlookers = newLocation.getAreaOnlookers(actor, null);
		actionResponse.addAffectedArea(newLocation);
		actionResponse.addAffectedArea(curLocation);
		String onlookerDesc = null;
		if (action.equalsIgnoreCase("Secret")) {
			for (GameCharacter ol : curAreaOnlookers) {
				if (!exit.knowsSecretExit(ol)) {
					onlookerDesc = " You now know how to get to the secret halls from here!";
					exit.knowsSecretExit(ol);
				}
				onlookerDesc = (onlookerDesc == null) ? exit.getExitOnlookerDescription(curLocation) : exit.getExitOnlookerDescription(curLocation) + onlookerDesc;
				actionResponse.addOnlooker(ol, onlookerDesc);
			}
			actionResponse.addOnlookers(newAreaOnlookers, actor.getName() + " enters from " + newLocation.getName());
		} else {
			actionResponse.addOnlookers(curAreaOnlookers, actor.getName() + " heads " + exitDir + " to " + newLocation.getName());
			if (exit.isHidden()) {
				onlookerDesc = actor.getName() + " enters from " + curLocation.getName() + ".";
				for (GameCharacter ol : newAreaOnlookers) {
					if (exit.knowsSecretExit(ol)) {
						actionResponse.addOnlookers(newAreaOnlookers, onlookerDesc);
					} else {
						onlookerDesc += " You now know how to get to the secret halls from here!";
						exit.addKnowsSecretExit(ol);
						actionResponse.addOnlookers(newAreaOnlookers, onlookerDesc);
					}
				}
			} else {
				actionResponse.addOnlookers(newAreaOnlookers, actor.getName() + " enters from " + exitDir + " in " + curLocation.getName());
			}
		}

		if (actor.getListeningToArea() != null) {
			newDesc.append("While moving, you are no longer able to listen to " + actor.getListeningToArea().getName() + ".\n");
		}
		
		if (actor.getListeningToCharacter() != null) {
			newDesc.append("While moving, you are no longer able to listen to " + actor.getListeningToCharacter().getName() + ".\n");
		}
		
		for (GameCharacter listeningCharacter : actor.getListeningCharacters()) {
			String genderText = (actor.getGender().equals(GameCharacter.Gender.MALE) ? "him" : "her");
			actionResponse.addOnlooker(listeningCharacter, actor.getName() + " heads " + exitDir + " to " + newLocation.getName() + ". You are no longer able to listen to " + genderText);
			actor.removeListener(listeningCharacter);
			listeningCharacter.stopListening();
		}
		
		if (actor.getListeningToCharacter() != null) {
			actor.getListeningToCharacter().removeListener(actor);
		}
		actor.stopListening();
		if (exit.isHidden()) {
			newDesc.append(exit.getExitDescription(curLocation));
		} else {
			newDesc.append("You leave the area headed ");
			newDesc.append(exitDir);
			newDesc.append(".\n\n");
		}
		newDesc.append(actor.getLocation().describeRoom(actor));
		actionResponse.setActionComplete();

		actionResponse.setActingCharacterDescription(newDesc.toString());
		actionComplete = true;
        return actionResponse;
    }
    
	public void reset() {
		actionResponse = null;
		actionComplete = false;
	}
	
    public ActionResponse getActionResponse() {
		return actionResponse;
	}

	public boolean isComplete() {
		return actionComplete;
	}
    
    public Set<String> getKeywords() {
        return keywords;
    }
}